/*
 *	ME2 C++ SDK
 *	Copyright (c) 2010 _silencer
 *
 *	Permission is hereby granted, free of charge, to any person
 *	obtaining a copy of this software and associated documentation
 *	files (the "Software"), to deal in the Software without
 *	restriction, including without limitation the rights to use,
 *	copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the
 *	Software is furnished to do so, subject to the following
 *	conditions:
 *
 *	The above copyright notice and this permission notice shall be
 *	included in all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *	OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef UNREAL_PROPERTY_H
#define UNREAL_PROPERTY_H

#include "unreal_base.h"
#include "unreal_field.h"
#include "unreal_enum.h"

namespace unreal
{
	#pragma pack(push)
	#pragma pack(1)

	/*
		If your compiler doesn't support 64 bit enums change it to defines or const int64...
		TODO write some dump code to check these flags from ut99 are still valid!
	*/
	enum uproperty_flags: int64
	{
		uproperty_edit				= 0x0000000000000001,
		uproperty_const				= 0x0000000000000002,
		uproperty_input				= 0x0000000000000004,
		uproperty_export_object		= 0x0000000000000008,
		uproperty_optional_parm		= 0x0000000000000010,
		uproperty_net				= 0x0000000000000020,
		uproperty_const_ref			= 0x0000000000000040,
		uproperty_parm				= 0x0000000000000080,
		uproperty_out_parm			= 0x0000000000000100,
		uproperty_skip_parm			= 0x0000000000000200,
		uproperty_return_parm		= 0x0000000000000400,
		uproperty_coerce_parm		= 0x0000000000000800,
		uproperty_native			= 0x0000000000001000,
		uproperty_transient			= 0x0000000000002000,
		uproperty_config			= 0x0000000000004000,
		uproperty_localized			= 0x0000000000008000,
		uproperty_travel			= 0x0000000000010000,
		uproperty_edit_const		= 0x0000000000020000,
		uproperty_global_config		= 0x0000000000040000,
		uproperty_on_demand			= 0x0000000000100000,
		uproperty_new				= 0x0000000000200000,
		uproperty_unknown_0			= 0x0000000000080000,
		uproperty_need_ctor_link	= 0x0000000000400000,
		uproperty_unknown_1			= 0x0000000000800000,
		uproperty_unknown_2			= 0x0000000002000000,
		uproperty_unknown_3			= 0x0000000004000000,
		uproperty_unknown_4			= 0x0000000008000000,
		uproperty_unknown_5			= 0x0000000010000000,
		uproperty_unknown_6			= 0x0000000020000000,
		uproperty_unknown_7			= 0x0000000040000000,
		uproperty_unknown_8			= 0x0000000100000000,
		uproperty_unknown_9			= 0x0000000200000000,
		uproperty_unknown_10		= 0x0000000400000000,

		uproperty_parm_flags			= uproperty_optional_parm | uproperty_parm | uproperty_out_parm | uproperty_skip_parm | uproperty_return_parm | uproperty_coerce_parm,
		uproperty_propagate_from_struct	= uproperty_const | uproperty_native | uproperty_transient
	};

	class uproperty: public ufield
	{
		DECLARE_CORE_CLASS( uproperty, ufield, Property )

	public:

		struct type_info
		{
			type_info()
				: referenced( false )
			{
			}

			type_info( std::string has_base, bool is_referenced = false )
				: base( has_base ), referenced( is_referenced )
			{
			}

			const std::string get_str( const std::string& reference_symbol = "" )
			{
				std::string result( base );

				if ( inner.size() > 0 )
				{
					result += "<";

					std::vector<type_info>::iterator it( inner.begin() );

					for ( ; it != inner.end(); ++it )
					{
						result += (*it).get_str( reference_symbol );

						if ( ( it + 1 ) != inner.end() )
							result += ", ";
					}

					result += ">";
				}

				if ( referenced )
					result += reference_symbol;

				return result;
			}

			std::string base;
			bool referenced;
			std::vector<type_info> inner;
		};

		uint32 get_array_dimension() const
		{
			return array_dim_;
		}

		uint32 get_element_size() const
		{
			return element_size_;
		}

		uint64 get_property_flags() const
		{
			return property_flags_;
		}

		uname get_category() const
		{
			return category_;
		}

		uint32 get_offset() const
		{
			return offset_;
		}

		static std::string& type_to_native( std::string& type_str );
		const type_info get_type() const;

	private:

		uint32 array_dim_;
		uint32 element_size_;
		uint64 property_flags_;
		uname category_;
		uint16 rep_offset_;
		uint16 rep_index_;
		uint32 unknown_0_[3];
		uint32 offset_;
		uproperty* property_link_next_;
		uproperty* config_link_next_;
		uproperty* constructor_link_next_;
		uproperty* next_ref_;
		uproperty* rep_owner_;
		uint32 unknown_1_[2];
	};

	class ubyteproperty: public uproperty
	{
		DECLARE_CORE_CLASS( ubyteproperty, uproperty, ByteProperty )

	public:

		uenum* get_enumeration() const
		{
			return enum_;
		}

		void set_enumeration( uenum* enumeration )
		{
			enum_ = enumeration;
		}

	private:

		uenum* enum_;
		uint32 unknown_;
	};

	class uintproperty: public uproperty
	{
		DECLARE_CORE_CLASS( uintproperty, uproperty, IntProperty )
	};

	class uboolproperty: public uproperty
	{
		DECLARE_CORE_CLASS( uboolproperty, uproperty, BoolProperty )

	public:

		uint32 get_bit_mask() const
		{
			return bit_mask_;
		}

		void set_bit_mask( const uint32 bit_mask )
		{
			bit_mask_ = bit_mask;
		}

	private:

		uint32 bit_mask_;
		uint32 unknown_;
	};

	class ufloatproperty: public uproperty
	{
		DECLARE_CORE_CLASS( ufloatproperty, uproperty, FloatProperty )
	};

	/* This base class is only for fixing the alignment of child classes, never use it directly... */
	class ubase_objectproperty: public uproperty
	{
	public:

		uclass* get_object_class() const
		{
			return object_class_;
		}

		void set_object_class( uclass* object_class )
		{
			object_class_ = object_class;
		}

	private:

		uclass* object_class_;
	};

	class uobjectproperty: public ubase_objectproperty
	{
		DECLARE_CORE_CLASS( uobjectproperty, uproperty, ObjectProperty )

	private:

		uint32 pad_;
	};

	class uclassproperty: public ubase_objectproperty
	{
		DECLARE_CORE_CLASS( uclassproperty, uobjectproperty, ClassProperty )

	public:

		uclass* get_meta_class() const
		{
			return meta_class_;
		}

		void set_meta_class( uclass* meta_class )
		{
			meta_class_ = meta_class;
		}

	private:

		uclass* meta_class_;
	};

	class unameproperty: public uproperty
	{
		DECLARE_CORE_CLASS( unameproperty, uproperty, NameProperty )
	};

	class uarrayproperty: public uproperty
	{
		DECLARE_CORE_CLASS( uarrayproperty, uproperty, ArrayProperty )

	public:

		uproperty* get_inner_property() const
		{
			return inner_;
		}

		void set_inner_property( uproperty* inner )
		{
			inner_ = inner;
		}

	private:

		uproperty* inner_;
		uint32 unknown_;
	};

	class umapproperty: public uproperty
	{
		DECLARE_CORE_CLASS( umapproperty, uproperty, MapProperty )

	public:

		uproperty* get_key_property() const
		{
			return key_;
		}

		uproperty* get_value_property() const
		{
			return value_;
		}

		void set_key_property( uproperty* key )
		{
			key_ = key;
		}

		void set_value_property( uproperty* value )
		{
			value_ = value;
		}

	private:

		uproperty* key_;
		uproperty* value_;
	};

	class ustrproperty: public uproperty
	{
		DECLARE_CORE_CLASS( ustrproperty, uproperty, StrProperty )
	};

	class ustructproperty: public uproperty
	{
		DECLARE_CORE_CLASS( ustructproperty, uproperty, StructProperty )

	public:

		ustruct* get_struct() const
		{
			return struct_;
		}

		void set_struct( ustruct* structure )
		{
			struct_ = structure;
		}

	private:

		ustruct* struct_;
		uint32 unknown_;
	};

	class ucomponentproperty: public ubase_objectproperty
	{
		DECLARE_CORE_CLASS( ucomponentproperty, uobjectproperty, ComponentProperty )

	private:

		uint32 unknown_;
	};

	/*
	TODO interface, delegate and stringref property
	*/
	class uinterfaceproperty: public uproperty
	{
		DECLARE_CORE_CLASS( uinterfaceproperty, uproperty, InterfaceProperty )

	private:

		uint32 unknown_[2];
	};

	class udelegateproperty: public uproperty
	{
		DECLARE_CORE_CLASS( udelegateproperty, uproperty, DelegateProperty )

	private:

		uint32 unknown_[2];
	};

	class ustringrefproperty: public uproperty
	{
		DECLARE_CORE_CLASS( ustringrefproperty, uproperty, StringRefProperty )
	};

	#pragma pack(pop)
}

#endif
